﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using mshtml;
using System.Threading;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Drawing;
using System.Collections.Concurrent;
using System.Reflection;
using System.Windows.Forms;
using AutoPageHelper;
using System.Drawing;

namespace AutoIE
{
    public class BasicProcessor : AutoIE.IProcessor, IDisposable
    {
        private AutoResetEvent threadEvent;
        private ConcurrentQueue<Page> pageQueue;
        private IEProxy IE;
        private IRcgEngine rcgEgn;
        private bool bRecord = false;
        public BasicProcessor()
        {
            if (threadEvent == null)
                threadEvent = new AutoResetEvent(false);
            pageQueue = new ConcurrentQueue<Page>();
            data = new ExcelSource();
            IE = new IEProxy();
            rcgEgn = new BasicRcgEngine();
        }
        public void StartRecord(string CaptureUrl, string RecordDirectory = "Record", bool bOpen = false)
        {
            DbgLn(CaptureUrl);
            string codeDemo = "//" + DateTime.Now.ToString() + " Start Recoding @ " + CaptureUrl;
            codeDemo += "\r\n //****** Code Demo ******";
            codeDemo += @"
//var url = @""http://www.sdu.edu.cn/"";
//Initialize(url, ""data.xlsx"", true);
//Data.MoveFirst();
//while (Data.MoveNext())
//{
//    var p = WaitUrl(""http://www.sdu.edu.cn/"");
//    var m1 = new string[] { Data[""用户""], ""uid"", Data[""密码""]};
//    p.Fill(m1);
//    p.Click(""enter"");
//    var p2 = WaitAnyUrl(new string[] { ""^http://sdu.edu.cn/coremail/xjs/index.jsp\\?sid=.*$"", ""http://sdu.edu.cn/coremail/login.jsp"" });
//    if (p2.Url.IndexOf(""login.jsp"")>0)
//    {
//        Data.Result = ""失败"";
//        OutputLine(Data[""用户""] + "":"" + Data[""密码""] + "" 失败"");
//        GoUrl(url);
//        continue;
//    }
//    else 
//    {
//        Data.Result = ""成功"";
//        OutputLine(Data[""用户""] + "":"" + Data[""密码""] + "" 成功"");
//        break;
//    }
//}
//Data.Save();";
            codeDemo += "\r\n //****** Code Demo ******";
            ActionRecorder.Default.SaveAction(codeDemo);
            Console.WriteLine("Recording @ " + CaptureUrl + "...");
            bRecord = true;
            Initialize(CaptureUrl, RecordDirectory, bOpen);
            WaitUrl("http://www.sdu.edu.cn/~caohai/");// not return until closed ie
        }
        public void Initialize(string CaptureUrl, string FilePath = "...", bool bOpen = false)
        {
            if (bOpen && !MyUtility.IsRegexPattern(FilePath))
            {
                OpenWindow(CaptureUrl);
                Wait(5000);
            }
            if (FilePath != "...")
                ActionRecorder.Default.RecordDirectory = FilePath;
            IE.Record = bRecord;
            if (IE.Bind(CaptureUrl))
            {
                IE.OnDocumentComplete += new IEEventHander(DocComplete);
                IE.OnClosed += new IEEventHander(IEQuit);
                
                if (!bRecord) // 不是录像就是执行
                {
                    if (FilePath == "...")
                    {
                        OpenFileDialog fg = new OpenFileDialog();
                        if (DialogResult.OK == fg.ShowDialog())
                            FilePath = fg.FileName;
                        else
                            FilePath = "";
                    }
                    DataFile = FilePath;
                }
            }
            else
            {
                throw new Exception("initialize failed, please open (" + CaptureUrl + ") with IE");
            }
        }
        #region IDisposable 成员
        private bool bDisposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (bDisposed) 
                return;
            if (disposing)
            {
                // 托管
                IE.Dispose();
            }
            // 非托管
            bDisposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
        ~BasicProcessor()
        {
            Dispose(false);
        }
        private void DocComplete(IHTMLDocument doc, string url)
        {
            pageQueue.Enqueue(new Page(doc, url));
            threadEvent.Set();
        }
        private void ReqComplete(IHTMLDocument doc, string url)
        {

        }
        private void IEQuit(IHTMLDocument doc, string url)
        {
            Console.WriteLine("IE was closed.");
            Environment.Exit(-2);
        }
        private ExcelSource data;
        public ExcelSource Data
        {
            get {
                if (string.IsNullOrEmpty(data.DefaultTableName))
                    return null;
                return data; 
            }
        }
        public string DataFile
        {
            set 
            {
                if(!string.IsNullOrEmpty(value))
                    data.Load(value);
            }
        }
        public IRcgEngine RcgEngine
        {
            set {rcgEgn = value;}
        }
        public string RcgEngineAssembly
        {
            set
            {
                Assembly asm = Assembly.LoadFrom(value);
                Type[] types = asm.GetTypes();
                Type rcgType = null;
                foreach (Type type in types)
                {
                    if (type is IRcgEngine)
                    {
                        rcgType = type;
                        break;
                    }
                }
                if (rcgType == null)
                    return;
                rcgEgn = asm.CreateInstance(rcgType.Name) as IRcgEngine;//Activator.CreateInstance(rcgType, true) 
            }
        }
        public bool RecordOnly
        {
            get { return bRecord; }
            set { bRecord = value; }
        }
        public virtual void Run()
        {
            throw new NotImplementedException();
        }

        
        public Page WaitUrl(string url)
        {
            DbgLn(url);
            Regex urlTemp = MyUtility.GetRegex(url);
            while (threadEvent.WaitOne())
            {
                Page p;
                while (pageQueue.TryDequeue(out p))
                {
                    if (urlTemp.IsMatch(p.Url))
                    {
                        DbgLnOk(p.Url);
                        return p;
                    }
                }
            }
            return null;
        }

        public Page WaitRequest(string url)
        {
            DbgLn(url);
            Regex urlTemp = MyUtility.GetRegex(url);
            while (threadEvent.WaitOne())
            {
                Page p;
                while (pageQueue.TryDequeue(out p))
                {
                    if (urlTemp.IsMatch(p.Url))
                    {
                        DbgLnOk(p.Url);
                        return p;
                    }
                }
            }
            return null;
        }

        public Page WaitAnyUrl(object[] urls)
        {
            DbgLn(string.Join("; ", urls));
            Regex[] urlTemps = new Regex[urls.Length];
            for (int i = 0; i < urls.Length; i++ )
            {
                urlTemps[i] = MyUtility.GetRegex(urls[i].ToString());
            }
            
            while (threadEvent.WaitOne())
            {
                List<Page> ps = pageQueue.ToList();
                List<string> purls = (from pq in ps select pq.Url).ToList();
                for (int i = 0; i < urls.Length; i++)
                {
                    for (int j = 0; j < purls.Count; j++ )
                    {
                        if (urlTemps[i].IsMatch(purls[j]))
                        {
                            DbgLnOk(purls[j]);
                            return ps[j];
                        }
                    }
                    
                }
            }
            DbgLnErr(string.Join(",", urls));
            return null;
        }
        public Page[] WaitAllUrl(object[] urls)
        {
            DbgLn(string.Join(",", urls));
            Regex[] urlTemps = new Regex[urls.Length];
            Page[] pr = new Page[urls.Length];
            for (int i = 0; i < urls.Length; i++)
            {
                urlTemps[i] = MyUtility.GetRegex(urls[i].ToString());
            }
           
            while (threadEvent.WaitOne())
            {
                List<Page> ps = pageQueue.ToList();
                List<string> purls = (from pq in ps select pq.Url).ToList();
                for (int i = 0; i < urls.Length; i++)
                {
                    if (urlTemps[i] == null || pr[i] != null)
                        continue;
                    for (int j = 0; j < purls.Count; j++)
                    {
                        if (urlTemps[i].IsMatch(purls[j]))
                        {
                            urlTemps[i] = null;
                            pr[i] = ps[j];
                            break;
                        }
                    }
                }
                if (urlTemps.All(t => t == null) && pr.All(p => p != null))
                {
                    DbgLnOk(string.Join(",", urls));
                    return pr;
                }
            }
            DbgLnErr(string.Join(",", urls));
            return null;
        }
        public void Wait()
        {
            DbgLn("document complete");
            threadEvent.WaitOne();
        }
        public void Wait(int milliseconds)
        {
            DbgLn(milliseconds.ToString());
            Thread.Sleep(milliseconds);
        }

        public void OpenWindow(string url)
        {
            DbgLn(url);
            if(url[0] != '^')
                Process.Start("IEXPLORE.EXE", url);
        }
        public void GoUrl(string url)
        {
            DbgLn(url);
            IE.MainWindow.Navigate(url);
        }
        public Page GotoUrl(string url)
        {
            DbgLn(url);
            IE.MainWindow.Navigate(url);
            return WaitUrl(url);
        }
        public void Post(string url, string postString)
        {
            DbgLn(url);
            Encoding enc;
            if (IE.MainWindow.Document != null)
                enc = Encoding.GetEncoding((IE.MainWindow.Document as IHTMLDocument2).defaultCharset);
            else
                enc = Encoding.Default;
            byte[] postData = enc.GetBytes(postString);
            IE.MainWindow.Navigate(url,Type.Missing, Type.Missing, postData);
        }
        public Page PostToUrl(string url, string postString)
        {
            Post(url, postString);
            return WaitUrl(url);
        }
        public string Recognise(Image img)
        {
            DbgLn("");
            if (rcgEgn != null)
                return rcgEgn.Recognize(img);
            return "";
        }
        public string Recognize(Image img)
        {
            return Recognise(img);
        }
        public void Output(string text)
        {
            DbgLn(text);
            Console.Write(text);
        }
        public void OutputLine(string text)
        {
            DbgLn(text);
            Console.WriteLine(text);
        }

        [Conditional("DEBUG")]
        void DbgLn(string text, int index = 1)
        {
            MethodInfo mi = new StackTrace().GetFrame(index).GetMethod() as MethodInfo;
            text = mi.Name + ": " + text + " @ Processor";
            Debug.WriteLine(text);
        }

        [Conditional("DEBUG")]
        void DbgLnOk(string text, string result = "")
        {
            if (string.IsNullOrEmpty(result))
                DbgLn(text + " [OK]", 2);
            else
                DbgLn(text + " -> " + result + " [OK]", 2);
        }
        [Conditional("DEBUG")]
        void DbgLnErr(string text)
        {
            DbgLn(text + " [ERROR]",2);
        }

        
    }
}
